using System;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Reflection.Emit;

using DogfoodRegexes=Jitsu.RegexC.RegularExpressions;

class CodeParser
{
	string sourcepath;

	public CodeParser(string sourcepath)
	{
		this.sourcepath = sourcepath;
	}

	public void ParseSourceFile(
		AssemblyName an,
		out RegexCompilationInfo[] regexinfos, 
		out CustomAttributeBuilder[] custattrs)
	{
		// Define our language elements with a set of simple regexes.
		Regex version = new DogfoodRegexes.VersionLine();
		Regex keyname = new DogfoodRegexes.KeynameLine();
		Regex keyfile = new DogfoodRegexes.KeyfileLine();
		Regex comment = new Regex(@"^#.*$");
		Regex regexline = new DogfoodRegexes.RegexLine();

		// Create a bucket for the regex entries.
		ArrayList regexes = new ArrayList();

		// Loop through the file, line by line.
		using (TextReader tr = new StreamReader(sourcepath,true))
		{
			while (true)
			{
				string line = tr.ReadLine();
				if (line==null) break;

				line = line.Trim();

				if (line == String.Empty)
					continue;
				if (comment.IsMatch(line))
					continue;

				if (regexline.IsMatch(line))
				{
					// Interpret the line as a regex entry.
					Match m = regexline.Match(line);
					string @namespace = m.Groups["namespace"].Value.Trim('.');
					string typename = m.Groups["typename"].Value;
					string[] options = m.Groups["options"].Value.Split('|');
					string pattern = m.Groups["pattern"].Value;

					RegexOptions regexoptions = RegexOptions.None;
					foreach (string option in options)
					{
						switch (option)
						{
							case "Compiled": regexoptions |= RegexOptions.Compiled; break;
							case "CultureInvariant": regexoptions |= RegexOptions.CultureInvariant; break;
							case "ECMAScript": regexoptions |= RegexOptions.ECMAScript; break;
							case "ExplicitCapture": regexoptions |= RegexOptions.ExplicitCapture; break;
							case "IgnoreCase": regexoptions |= RegexOptions.IgnoreCase; break;
							case "IgnorePatternWhitespace": regexoptions |= RegexOptions.IgnorePatternWhitespace; break;
							case "Multiline": regexoptions |= RegexOptions.Multiline; break;
							case "None": regexoptions |= RegexOptions.None; break;
							case "RightToLeft": regexoptions |= RegexOptions.RightToLeft; break;
							case "Singleline": regexoptions |= RegexOptions.Singleline; break;
							default: throw new ApplicationException("Unrecognized regex option: "+option);
						}
					}

					RegexCompilationInfo rci = new RegexCompilationInfo(
						pattern,regexoptions,typename,@namespace,true);
					regexes.Add(rci);
				}
				else if (version.IsMatch(line))
				{
					// Interpret the line as a version declaration.
					Match m = version.Match(line);
					string @value = m.Groups["value"].Value;
					an.Version = new Version(@value);
				}
				else if (keyname.IsMatch(line))
				{
					// Interpret the line as a strongname key declaration.
					Match m = keyname.Match(line);
					string @value = m.Groups["value"].Value;
					an.KeyPair = new StrongNameKeyPair(@value);
				}
				else if (keyfile.IsMatch(line))
				{
					// Interpret the line as a strongname key declaration (but only if it 
					// hasn't already been set by a keyname attribute).
					if (an.KeyPair.PublicKey == null)
					{
						Match m = keyfile.Match(line);
						string @value = m.Groups["value"].Value;
						using (FileStream fs = File.OpenRead(@value))
						{
							an.KeyPair = new StrongNameKeyPair(fs);
						}
					}
				}
				else throw new ApplicationException("Syntax error: "+line);
			}
		}
		regexinfos = (RegexCompilationInfo[])regexes.ToArray(typeof(RegexCompilationInfo));
		custattrs = null; //todo
		return;
	}

	//	CustomAttributeBuilder[] ParseCustomAttributes()
	//	{
	//		// Prepare [AssemblyVersion(string)] attribute.
	//		Type t = typeof(System.Reflection.AssemblyVersionAttribute);
	//		ConstructorInfo ci = t.GetConstructor(new Type[] { typeof(string) });
	//
	//		CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[] { "1.2.3.4" });
	//
	//		return new CustomAttributeBuilder[] { cab };
	//	}
}